from datetime import datetime
import os
import platform
import re
import threading
from tkinter import ttk
import multiprocessing

import matplotlib
import serial

import controller
import data
import graph
import JY901S
import process as pro
import view
import tkinter as tk
import tkinter.messagebox
from matplotlib.pylab import mpl
from tkinter import simpledialog


if platform.system().lower() == 'Windows':
    mpl.rcParams['font.sans-serif'] = ['SimHei']  # 中文显示
mpl.rcParams['axes.unicode_minus'] = False  # 负号显示
global con, num


def get_user_input():
    def on_submit():
        high_blood_pressure = entry_high.get()
        low_blood_pressure = entry_low.get()
        heart_rate = entry_heart_rate.get()
        real_data.append(high_blood_pressure)
        real_data.append(low_blood_pressure)
        real_data.append(heart_rate)
        root.destroy()

    real_data = []

    # 创建主窗口
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口

    # 创建输入窗口
    newWin = tk.Toplevel(root)
    newWin.title("输入血压和心率")

    # 创建输入标签和输入框
    tk.Label(newWin, text="请输入高血压值:").pack()
    entry_high = tk.Entry(newWin)
    entry_high.pack()

    tk.Label(newWin, text="请输入低血压值:").pack()
    entry_low = tk.Entry(newWin)
    entry_low.pack()

    tk.Label(newWin, text="请输入心率值:").pack()
    entry_heart_rate = tk.Entry(newWin)
    entry_heart_rate.pack()

    # 创建提交按钮
    submit_button = tk.Button(newWin, text="提交", command=on_submit)
    submit_button.pack()

    root.mainloop()  # 启动主事件循环

    return real_data


def extract_number(s):
    match = re.search(r'\d+', s)
    if match:
        return int(match.group())
    else:
        return None


def read_PPG():
    global con
    # 设置串口参数
    if (platform.system().lower() == 'linux'):
        port = '/dev/ttyUSB0'  # 设置串口   Set serial port
    else:
        port = 'COM7'  # 设置串口   Set serial port
    baudrate = 115200
    ser = serial.Serial(port, baudrate)
    while 1:
        if ser.in_waiting:
            try:
                data = ser.readline().decode('utf-8').strip()
                con.data.collect_ppg_data(extract_number(data))
            except ValueError:
                continue


def updateLabel(label, data):
    pass


def updateWindow():
    for g in con.view.graph:
        g.updateMeltGraph()
    win.after(100, updateWindow)  # 1000ms更新画布


def updateLabelData(label, data):
    # print(data)
    label.config(text=f'x: {data[0]:.2f} y: {data[1]:.2f} z: {data[2]:.2f}')


def updateLabelValue(label, data):
    value = f"{data:.2f}"
    label.config(text=value)


def get_latest_folder(base_path):
    print(base_path)
    # 获取文件夹下所有文件/文件夹的名称
    all_entries = os.listdir(base_path)

    # 过滤掉非文件夹的条目，并将符合时间格式的文件夹名称存储在一个字典中
    time_format = '%Y-%m-%d-%H-%M-%S'
    folders = {}

    for entry in all_entries:
        entry_path = os.path.join(base_path, entry)
        if os.path.isdir(entry_path):
            try:
                folder_time = datetime.strptime(entry, time_format)
                folders[folder_time] = entry_path
            except ValueError:
                # 忽略不符合时间格式的文件夹
                pass

    # 获取最新的文件夹
    if not folders:
        return None
    latest_folder_time = max(folders)
    return folders[latest_folder_time]


def save_to_txt(file_path, file_name, data):
    # 创建文件夹路径（如果不存在）
    if not os.path.exists(file_path):
        os.makedirs(file_path)

    # 完整文件路径
    full_path = os.path.join(file_path, file_name)

    # 创建文件并写入数据
    with open(full_path, 'w') as file:
        for item in data:
            file.write(f'{item}\n')


def read_from_txt(file_path, file_name):
    # 完整文件路径
    full_path = os.path.join(file_path, file_name)
    with open(full_path, 'r') as f:
        data = [float(line.strip()) for line in f]
    return data


def get_user_name():
    global con
    # 获取用户输入信息
    user_name = con.view.txtBoxes[0]['text'].get('1.0', tk.END).rstrip('\n')
    if user_name == '':
        tkinter.messagebox.showerror(title='出错', message='请输入用户信息')
        return ''
    con.data.user_name = user_name
    return user_name


def get_user_information(user_name):
    # 获取用户数据信息
    folder_path = './data/' + user_name
    file_path = os.path.join(folder_path, 'data.txt')

    # 检查文件夹是否存在，如果不存在则创建
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)

    # 检查文件是否存在
    if not os.path.exists(file_path):
        tkinter.messagebox.showwarning(title='出错', message='没有用户ptt拟合数据，将使用默认数据')
        return
    else:
        # 如果文件存在，则按行读取数据
        with open(file_path, 'r') as file:
            user_data = [float(line.strip()) for line in file]

        if len(user_data) > 3:
            con.data.high_a = user_data[0]
            con.data.high_b = user_data[1]
            con.data.low_a = user_data[2]
            con.data.low_b = user_data[3]


def read_specific_lines_in_folders(base_folder, filename, line_number):
    result_list = []

    # 遍历 base_folder 下的所有子文件夹
    for root, dirs, files in os.walk(base_folder):
        # 检查是否包含指定的 txt 文件
        if filename in files:
            file_path = os.path.join(root, filename)
            # 读取指定行的数据
            with open(file_path, 'r') as file:
                # 读取指定行的数据
                lines = file.readlines()
                if len(lines) >= line_number:
                    data = lines[line_number - 1].strip()
                    # 将数据转换为数字并添加到列表中
                    result_list.append(float(data))  # 假设数据是浮点数

    return result_list


def timer_callback():
    global con
    con.data.mode = 2
    con.data.timer = 0
    if con.mode == 0:
        # 保存数据文件
        file_path = './data/' + con.data.user_name + '/' + datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
        save_to_txt(file_path, 'ppg.txt', con.data.ppg_value)
        save_to_txt(file_path, 'acceleration_z.txt', con.data.acceleration_z_value)
        save_to_txt(file_path, 'angular_velocity_z.txt', con.data.angular_velocity_z_value)
        save_to_txt(file_path, 'angular_velocity_x.txt', con.data.angular_velocity_x_value)
        save_to_txt(file_path, 'scg_time.txt', con.data.scg_time)
        save_to_txt(file_path, 'ppg_time.txt', con.data.ppg_time)

        real_data = get_user_input()

        save_to_txt(file_path, 'real_data.txt', real_data)

        # 计算波峰列表
        # 计算腕部绿光
        con.data.ppg_index, con.data.ppg_error_num, con.data.ppg_threshold = pro.ppg(con.data.ppg_value, 0)
        # 计算加速度z和角速度z并比较
        con.data.scg_acc_z_index, con.data.scg_acc_z_error_num, con.data.scg_acc_z_threshold = pro.scg(
            con.data.acceleration_z_value, con.data.ppg_threshold)
        con.data.scg_ang_z_index, con.data.scg_ang_z_error_num, con.data.scg_ang_z_threshold = pro.scg(
            con.data.angular_velocity_z_value, con.data.ppg_threshold)
        con.data.scg_final_error_num, con.data.scg_final_index = (con.data.scg_acc_z_error_num,
                                                                  con.data.scg_acc_z_index) if con.data.scg_acc_z_error_num < con.data.scg_ang_z_error_num else (
            con.data.scg_ang_z_error_num, con.data.scg_ang_z_index)
        # 计算角速度x并比较
        con.data.scg_ang_x_index, con.data.scg_ang_x_error_num, con.data.scg_ang_x_threshold = pro.scg(
            con.data.angular_velocity_x_value, con.data.ppg_threshold)
        con.data.scg_final_error_num, con.data.scg_final_index = (con.data.scg_ang_x_error_num,
                                                                  con.data.scg_ang_x_index) if con.data.scg_ang_x_error_num < con.data.scg_final_error_num else (
            con.data.scg_final_error_num, con.data.scg_final_index)

        # 心率计算
        con.data.heart_rate = pro.heart_rate_cal(
            con.data.scg_time, con.data.scg_final_index)
        save_to_txt(file_path, 'index.txt', con.data.scg_final_index)

        # 血压计算
        con.data.ptt = pro.calculate_ptt(con.data.ppg_index, con.data.scg_final_index, con.data.ppg_time,
                                         con.data.scg_time)

        save_to_txt(file_path, 'ptt.txt', [con.data.ptt])

        con.data.hypertension = con.data.high_a / con.data.ptt + con.data.high_b
        con.data.hypotension = con.data.low_a / con.data.ptt + con.data.low_b

    elif con.mode == 1:
        print('开始分析')
        # 读取最新数据并进行数据展示
        latest_folder = get_latest_folder('./data/' + con.data.user_name)
        print(latest_folder)
        if latest_folder:

            con.data.ppg_value = read_from_txt(latest_folder, 'ppg.txt')
            con.data.acceleration_z_value = read_from_txt(latest_folder, 'acceleration_z.txt')
            con.data.angular_velocity_z_value = read_from_txt(latest_folder, 'angular_velocity_z.txt')
            con.data.angular_velocity_x_value = read_from_txt(latest_folder, 'angular_velocity_x.txt')
            con.data.ppg_time = read_from_txt(latest_folder, 'ppg_time.txt')
            con.data.scg_time = read_from_txt(latest_folder, 'scg_time.txt')

            # 计算波峰列表
            # 计算腕部绿光
            con.data.ppg_index, con.data.ppg_error_num, con.data.ppg_threshold = pro.ppg_analysis(con.data.ppg_value, 0, './data/' + con.data.user_name+'/ppg')
            # 计算加速度z和角速度z并比较
            con.data.scg_acc_z_index, con.data.scg_acc_z_error_num, con.data.scg_acc_z_threshold = pro.scg_analysis(
                con.data.acceleration_z_value, con.data.ppg_threshold, './data/' + con.data.user_name+'/acceleration_z')
            con.data.scg_ang_z_index, con.data.scg_ang_z_error_num, con.data.scg_ang_z_threshold = pro.scg_analysis(
                con.data.angular_velocity_z_value, con.data.ppg_threshold, './data/' + con.data.user_name+'/angular_velocity_z')
            con.data.scg_final_error_num, con.data.scg_final_index = (con.data.scg_acc_z_error_num,
                                                                      con.data.scg_acc_z_index) if con.data.scg_acc_z_error_num < con.data.scg_ang_z_error_num else (
                con.data.scg_ang_z_error_num, con.data.scg_ang_z_index)
            # 计算角速度x并比较
            con.data.scg_ang_x_index, con.data.scg_ang_x_error_num, con.data.scg_ang_x_threshold = pro.scg_analysis(
                con.data.angular_velocity_x_value, con.data.ppg_threshold, './data/' + con.data.user_name+'/angular_velocity_x')
            con.data.scg_final_error_num, con.data.scg_final_index = (con.data.scg_ang_x_error_num,
                                                                      con.data.scg_ang_x_index) if con.data.scg_ang_x_error_num < con.data.scg_final_error_num else (
                con.data.scg_final_error_num, con.data.scg_final_index)

            # 心率计算
            con.data.heart_rate = pro.heart_rate_cal(
                con.data.scg_time, con.data.scg_final_index)

            # 血压计算
            con.data.ptt = pro.calculate_ptt(con.data.ppg_index, con.data.scg_final_index, con.data.ppg_time,
                                             con.data.scg_time)

            con.data.hypertension = con.data.high_a / con.data.ptt + con.data.high_b
            con.data.hypotension = con.data.low_a / con.data.ptt + con.data.low_b
        else:
            tkinter.messagebox.showwarning(title='出错', message='还没有采集数据无法查看最新数据')

    elif con.mode == 2:
        ptt = read_specific_lines_in_folders('./data/' + con.data.user_name, 'ptt.txt', 1)
        hypertension = read_specific_lines_in_folders('./data/' + con.data.user_name, 'real_data.txt', 1)
        hypotension = read_specific_lines_in_folders('./data/' + con.data.user_name, 'real_data.txt', 2)

        if len(ptt) < 5:
            tkinter.messagebox.showwarning(title='警告', message='数据量太少不宜拟合')
            return

        ptt = [1 / x for x in ptt if x != 0]

        con.data.high_a, con.data.high_b = pro.linear_least_squares_fit(ptt, hypertension)
        con.data.low_a, con.data.low_b = pro.linear_least_squares_fit(ptt, hypotension)

        save_to_txt('./data/' + con.data.user_name, 'data.txt', [con.data.high_a, con.data.high_b, con.data.low_a, con.data.low_b])

        tkinter.messagebox.showinfo(title='成功', message='拟合完成')


def start_timer(seconds):
    timer = threading.Timer(seconds, timer_callback)
    timer.start()


def detection():
    global con
    user_name = get_user_name()
    get_user_information(user_name)
    con.mode = 0
    con.data.delete_data()
    con.data.mode = 1
    start_timer(60)


def analysis():
    global con
    user_name = get_user_name()
    get_user_information(user_name)
    con.mode = 1
    con.data.delete_data()
    con.data.mode = 0
    start_timer(1)


def fitting():
    global con
    user_name = get_user_name()
    get_user_information(user_name)
    con.mode = 2
    con.data.delete_data()
    con.data.mode = 0
    start_timer(1)


if __name__ == '__main__':
    num = 0
    # 创建控制器
    con = controller.Controller()
    # 创建数据模型
    data_instance = data.Data()

    jy901s_instance = JY901S.JY901S(data_instance)
    # 将模型加入控制器
    con.add_data(data_instance)
    con.add_device('jy901s', jy901s_instance)

    # 创建界面类
    win = tk.Tk()
    view_instance = view.View(win)
    con.add_view(view_instance)
    con.view.initWindow('健康监测', '1000x700')

    # 将数据绑定给界面
    view_instance.add_data(data_instance)

    # 创建标签
    con.view.addLabel('0', 'gray', ('Arial', 15), 10, 1, 0, updateLabelValue, 150, 10)
    con.view.addLabel('0', 'gray', ('Arial', 15), 10, 1, 1, updateLabelValue, 150, 50)
    con.view.addLabel('0', 'gray', ('Arial', 15), 10, 1, 2, updateLabelValue, 150, 90)
    con.view.addLabel('0', 'gray', ('Arial', 15), 10, 1, 3, updateLabelValue, 150, 130)
    con.view.addLabel('计时器', 'gray', ('Arial', 15), 10, 1, 4, updateLabel, 10, 10)
    con.view.addLabel('心率', 'gray', ('Arial', 15), 10, 1, 5, updateLabel, 10, 50)
    con.view.addLabel('高血压', 'gray', ('Arial', 15), 10, 1, 6, updateLabel, 10, 90)
    con.view.addLabel('低血压', 'gray', ('Arial', 15), 10, 1, 7, updateLabel, 10, 130)

    # 添加按钮
    con.view.addButton('开始检测', 'gray', ('Arial', 15), 10, 1, 8, 800, 10, detection)
    con.view.addButton('开始分析', 'gray', ('Arial', 15), 10, 1, 9, 800, 50, analysis)
    con.view.addButton('开始拟合', 'gray', ('Arial', 15), 10, 1, 9, 800, 90, fitting)

    # 添加输入框
    con.view.addTxtBox(('Arial', 15), 10, 1, 10, 300, 10)

    # 创建并启动线程读取ppg
    thread = threading.Thread(target=read_PPG)
    con.add_thread('ppg', thread)
    con.thread['ppg'].daemon = True  # 设置为守护线程，这样在主线程结束后子线程也会结束
    con.thread['ppg'].start()

    # 创建图表框架
    graph_frame = ttk.Frame(win, height=20, width=50)  # 创建图表控件
    graph_frame.place(x=10, y=180)

    # 创建图表
    graph_instance = graph.Graph(graph_frame, 4, 9, '数据预览')

    # 图表绑定data
    graph_instance.add_data(data_instance)

    # 绑定图表
    view_instance.add_graph(graph_instance)

    updateWindow()
    con.view.loop()
